﻿using System.Diagnostics;

namespace ConsoleApp1
{
  internal class Program
  {
    static Random random = new Random();
    static int[] delay = new int[] { 300, 400, 500, 600, 700, 800, 900, 1000, 1500, 2000, 3000 };
    static void LongRunningFoo()
    {
      Console.WriteLine("Task Start");
      var r = random.Next(0, 11);
      Console.WriteLine($"预计用时：{delay[r]}");
      Thread.Sleep(delay[r]);
      Console.WriteLine("Task down");
    }
    static void Main(string[] args)
    {

      Task.Factory.StartNew(async () =>
      {

        var start = Stopwatch.StartNew();

        while (true)
        {
          Thread.Sleep(10);
          start.Restart();
          CancelableThreadTask threadTask = new CancelableThreadTask(LongRunningFoo);
          //threadTask.Action = LongRunningFoo;
          CancellationTokenSource cts = new CancellationTokenSource(1000);

          try
          {
            await threadTask.RunAsync(cts.Token);
          }
          catch (Exception)
          {
            Console.WriteLine("task is cancel");
          }


          Console.WriteLine($"用时：{start.ElapsedMilliseconds}ms");
          Console.WriteLine();
          cts.Dispose();
        }
      }, TaskCreationOptions.LongRunning);
      Console.WriteLine("wait");
      Console.ReadLine();
    }
  }
  public class CancelableThreadTask
  {
    private Thread? _thread;
    private Action _action;
    private readonly Action<Exception>? _onError;
    private readonly Action? _onCompleted;
    private TaskCompletionSource? _tcs;

    private int _isRunning = 0;

    public Action Action { get => _action; set => _action = value; }

    public CancelableThreadTask(Action action, Action<Exception>? onError = null, Action? onCompleted = null)
    {
      Action = action ?? throw new ArgumentNullException(nameof(action));
      _onError = onError;
      _onCompleted = onCompleted;
    }

    public Task RunAsync(CancellationToken token)
    {
      if (Interlocked.CompareExchange(ref _isRunning, 1, 0) == 1)
        throw new InvalidOperationException("Task is already running");

      _tcs = new TaskCompletionSource();
      _thread = new Thread(() =>
      {
        try
        {
          Action();
          _tcs.SetResult();
          _onCompleted?.Invoke();
        }
        catch (Exception ex)
        {
          //if (ex is ThreadInterruptedException)
          //  _tcs.TrySetCanceled(token);
          //else
          //  _tcs.TrySetException(ex);
          //_onError?.Invoke(ex);
        }
        finally
        {
          Interlocked.Exchange(ref _isRunning, 0);
        }
      });

      token.Register(() =>
      {
        if (Interlocked.CompareExchange(ref _isRunning, 0, 1) == 1)
        {
          _thread.Interrupt();
          _thread.Join();
          _tcs.TrySetCanceled(token);
        }
      });

      _thread.Start();

      return _tcs.Task;
    }
  }
}
